package com.atguigu.gmall.cart.lisenter;

import com.atguigu.gmall.cart.bean.CartInfo;
import com.atguigu.gmall.cart.service.CartAsyncService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

@Component
public class CartMqListener {
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    CartAsyncService cartAsyncService;
    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = "cart.item.queue"),
                    exchange = @Exchange(value = "order.exchange" ,
                            type = ExchangeTypes.TOPIC , ignoreDeclarationExceptions = "true"),
                    key = "cart.item.delete"
            )
    })
    public void cartItemDelListener(Map<String,Object> map , Message message,
                                         Channel channel) throws IOException {//skuId:xx 、price:xxx

        try {
            //userId   skuIds
            String userId = map.get("userId").toString();
            List<String> skuIds = (List<String>) map.get("skuIds");
            //查找userId的购物车
            BoundHashOperations hashOps = redisTemplate.boundHashOps("cart:" + userId);
            //遍历购物车所有的购物项，如果购物项的skuid在skuIds的集合中 删除该购物项
            if(hashOps.size()>0){
                hashOps.keys().forEach(skuId->{
                    //删除redis中缓存的购物项
                    if(skuIds.contains(skuId.toString())){
                        hashOps.delete(skuId.toString());
                    }
                    //删除数据库中的购物项
                    cartAsyncService.deleteCartInfoByUserIdAndSkuId(userId,skuId.toString());
                });
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag() , false);
        } catch (Exception e) {
            e.printStackTrace();
            if(message.getMessageProperties().isRedelivered()){
                //丢弃消息
                channel.basicReject(message.getMessageProperties().getDeliveryTag() , false);
            }else{
                //第一次消费失败  重新归队
                channel.basicNack(message.getMessageProperties().getDeliveryTag() ,
                        false,true);
            }
        }
    }








    @RabbitListener(bindings = {
           @QueueBinding(
                   value = @Queue(value = "cart.price.queue"),
                   exchange = @Exchange(value = "cart.price.exchange" ,
                    type = ExchangeTypes.TOPIC , ignoreDeclarationExceptions = "true"),
                   key = "cart.price.current"
           )
    })
    public void cartCurrentPriceListener(Map<String,Object> map , Message message,
                                         Channel channel) throws IOException {//skuId:xx 、price:xxx

        try {
            // 以后存入skuId以字符串来存储
            String skuId = map.get("skuId").toString();
            BigDecimal price = new BigDecimal(map.get("price").toString());
            //更新redis中skuId的价格
            redisTemplate.opsForValue().set("cart:current:"+skuId , price);
            channel.basicAck(message.getMessageProperties().getDeliveryTag() , false);
        } catch (Exception e) {
            e.printStackTrace();
            if(message.getMessageProperties().isRedelivered()){
                //丢弃消息
                channel.basicReject(message.getMessageProperties().getDeliveryTag() , false);
            }else{
                /*  保证重要消息不丢失：
                        1、消息连续两次消费失败
                        2、丢弃消息到死信队列
                        3、可以考虑死信队列绑定延迟队列再次消费
                        4、如果一直失败 延迟队列也可以绑定死信队列
                        5、最后延迟队列的死信队列中的消息 人工干预
                 */
                //第一次消费失败  重新归队
                channel.basicNack(message.getMessageProperties().getDeliveryTag() ,
                        false,true);
            }
        }
    }

}
